#ifndef MeshAdjacency_h__
#define MeshAdjacency_h__


#include "Algorithm.hpp"
#include "GeometryTypes.hpp"
#include "Mesh.hpp"

class MeshAdjacency
{
   public:
      typedef tiny_vector<size_t, 3> AdjacentFaces;
      typedef std::map<Edge, AdjacentFaces> EdgeAdjacencyMap;
      MeshAdjacency(Mesh  aMeshPtr,
                    int   aMaxFaces=64);
      void Rebuild(const Mesh& aMesh);
      // Returns vertices connected to aVertex via an edge
      int GetVertexStar(int  aVertex,
                        int* aIndices)
      {
         int vertexTotal(0);
         int index = aVertex * mMaxFaces;
         for (int i = 0; i < mMaxFaces && mVertexData[index] != -1; ++i)
         {
            int faceIndex = mVertexData[index];
            const Face& f = mMesh.faces[faceIndex];
            if (f.vertex[0] != aVertex) aIndices[vertexTotal++] = f.vertex[0];
            if (f.vertex[1] != aVertex) aIndices[vertexTotal++] = f.vertex[1];
            if (f.vertex[2] != aVertex) aIndices[vertexTotal++] = f.vertex[2];
            ++index;
         }
         Algorithm::ShellSort(aIndices, vertexTotal);
         return std::unique(aIndices, aIndices + vertexTotal) - aIndices;
      }

      // Returns faces connected to aVertex
      int GetVertexFaces(int  aVertex,
                         int* aFaces)
      {
         int outInd=0;
         int index = aVertex * mMaxFaces;
         for (int i = 0; i < mMaxFaces; ++i)
         {
            if (mVertexData[index] == -1) break;
            aFaces[outInd++] = mVertexData[index++];
         }
         return outInd;
      }  

      // returns all faces sharing an edge
      int GetEdgeFaces(const Edge& aEdge,
                       int*        aFaces)
      {
         int outInd=0;
         int index = aEdge.first * mMaxFaces;
         for (int i = 0; i < mMaxFaces; ++i)
         {
            if (mVertexData[index] == -1) break;
            int triIndex = mVertexData[index++];
            Face& f = mMesh.faces[triIndex];
            if (  f.vertex[0] == aEdge.second
               || f.vertex[1] == aEdge.second
               || f.vertex[2] == aEdge.second)
            {
               aFaces[outInd++] = triIndex;
            }
         }
         return outInd;
      }  
      void GetVertexAdjacentFaces(const IndexList& aVertices,
                                  IndexList&       aAdjacentFaces);
      EdgeAdjacencyMap& EdgeAdjacency() { return mEdgeData; }

      Mesh& GetMesh() { return mMesh; }
   private:

      void BuildEdgeAdjacency();
      void InsertLink(int aFace,
                      int aVertex)
      {
         int index = aVertex * mMaxFaces;
         for (int i = 0; i < mMaxFaces; ++i)
         {
            if (mVertexData[index] == -1)
            {
               mVertexData[index] = aFace;
               return;
            }
            ++index;
         }
         std::cerr << "Too many faces.\n";
         exit(1);
      }

      
      

      bool  mEdgeBuilt;
      Mesh  mMesh;

      int               mMaxFaces;
      std::vector<int>  mVertexData;
      EdgeAdjacencyMap  mEdgeData;
};
#endif // MeshAdjacency_h__
